package com.fangcang.mongodb.impl;

import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.conversions.Bson;

import com.fangcang.mongodb.config.MongoConfig;
import com.fangcang.mongodb.model.PageResult;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.ServerAddress;
import com.mongodb.client.AggregateIterable;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;

/**
 * Mongodb操作Service类
 * 
 * @author fangcang
 * @date 2019年2月28日
 * @description
 */
public class MongoDbApiService {

	private MongoClient mongoClient;

	private MongoDatabase database;

	private MongoConfig mongoConfig;

	public MongoDbApiService(MongoConfig mongoConfig) {
		this.mongoConfig = mongoConfig;
		ServerAddress serverAddress = new ServerAddress(mongoConfig.getHost(), mongoConfig.getPort());
//		MongoCredential mongoCredential = MongoCredential.createScramSha1Credential(mongoConfig.getUserName(),
//				mongoConfig.getDbName(), mongoConfig.getPassword().toCharArray());

		CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
				fromProviders(PojoCodecProvider.builder().automatic(true).build()));
		CodecRegistry codecRegistry = CodecRegistries.fromRegistries(pojoCodecRegistry);

		MongoClientOptions clientOptions = MongoClientOptions.builder().codecRegistry(codecRegistry).build();
//		mongoClient = new MongoClient(serverAddress, mongoCredential, clientOptions);
		mongoClient = new MongoClient(serverAddress, clientOptions);

		database = mongoClient.getDatabase(mongoConfig.getDbName());
	}

	/**
	 * 写入一个对象
	 * 
	 * @param collectionName
	 * @param t              可为任意pojo、json字符串或map对象
	 */
	public <T> void insertOne(String collectionName, T t, Class<T> clazz) {
		getCollection(collectionName, clazz).insertOne(t);
	}

	/**
	 * 写入一个对象
	 * 
	 * @param collectionName
	 * @param t
	 */
	public <T> void insertOne(T t, Class<T> clazz) {
		insertOne(mongoConfig.getCollectionName(), t, clazz);
	}
	
	/**
	 * 写入多个对象
	 * 
	 * @param documents
	 */
	public <T> void insertMany(String collectionName, List<T> documents, Class<T> clazz) {
		if (documents == null || documents.isEmpty()) {
			return;
		}
		getCollection(collectionName, clazz).insertMany(documents);
	}

	/**
	 * 写入多个对象
	 * 
	 * @param documents
	 */
	public <T> void insertMany(List<T> documents, Class<T> clazz) {
		insertMany(mongoConfig.getCollectionName(), documents, clazz);
	}

	/**
	 * 更新单个对象
	 * 
	 * @param filter
	 * @param update
	 */
	public <T> long updateOne(String collectionName, Bson filter, Bson update, Class<T> clazz) {
		return getCollection(collectionName, clazz).updateOne(filter, update)
				.getModifiedCount();
	}

	
	/**
	 * 更新单个对象
	 * 
	 * @param filter
	 * @param update
	 */
	public  UpdateResult updateOne(Map<String, Object> filter, Map<String, Object> update) {
		return updateOne(mongoConfig.getCollectionName(), filter, update);
	}
	
	/**
	 * 更新单个对象
	 * 
	 * @param filter
	 * @param update
	 */
	public  UpdateResult updateOne(String collectionName, Map<String, Object> filter, Map<String, Object> update) {
		List<Bson> filters = getFilterList(filter);
		List<Bson> updates = getUpdateList(update);
		return getCollection(collectionName).updateOne(Updates.combine(filters),Updates.combine(updates));
	}
	
	private List<Bson> getFilterList(Map<String, Object> map){
		List<Bson> filterList = new ArrayList<Bson>();
		if(map == null || map.isEmpty()) {
			return filterList;
		}
		Set<Entry<String, Object>> entrySet = map.entrySet();
		for(Entry<String, Object> entry : entrySet) {
			filterList.add(Filters.eq(entry.getKey(), entry.getValue()));
		}
		return filterList;
	}
	
	private List<Bson> getUpdateList(Map<String, Object> map){
		List<Bson> updateList = new ArrayList<Bson>();
		if(map == null || map.isEmpty()) {
			return updateList;
		}
		Set<Entry<String, Object>> entrySet = map.entrySet();
		for(Entry<String, Object> entry : entrySet) {
			updateList.add(Updates.set(entry.getKey(), entry.getValue()));
		}
		return updateList;
	}

	/**
	 * 更新单个对象
	 * 
	 * @param filter
	 * @param update
	 */
	public UpdateResult updateMany(String collectionName, Map<String, Object> filter, Map<String, Object> update) {
		List<Bson> filters = getFilterList(filter);
		List<Bson> updates = getUpdateList(update);
		return getCollection(collectionName).updateMany(Updates.combine(filters), Updates.combine(updates));
	}

	/**
	 * 更新单个对象
	 * 
	 * @param filter
	 * @param update
	 */
	public  UpdateResult updateMany(Map<String, Object> filter, Map<String, Object> update) {
		return updateMany(mongoConfig.getCollectionName(), filter, update);
	}

	/**
	 * 删除单个对象
	 * 
	 * @param filter
	 * @return
	 */
	public DeleteResult deleteOne(String collectionName, Map<String, Object> filter) {
		List<Bson> filters = getFilterList(filter);
		return getCollection(collectionName).deleteOne(Updates.combine(filters));
	}

	/**
	 * 删除单个对象
	 * 
	 * @param filter
	 * @return
	 */
	public DeleteResult deleteOne(Map<String, Object> filter) {
		return deleteOne(mongoConfig.getCollectionName(), filter);
	}

	/**
	 * 删除多个对象
	 * 
	 * @param filter
	 * @return
	 */
	public  DeleteResult deleteMany(String collectionName, Map<String, Object> filter) {
		List<Bson> filters = getFilterList(filter);
		return getCollection(collectionName).deleteMany(Updates.combine(filters));
	}

	/**
	 * 删除多个对象
	 * 
	 * @param filter
	 * @return
	 */
	public DeleteResult deleteMany(Map<String, Object> filter) {
		return deleteMany(mongoConfig.getCollectionName(), filter);
	}

	/**
	 * 查询单个对象
	 * 
	 * @param query
	 * @param clazz
	 * @return
	 */
	public <T> T findOne(List<Bson> filters, Class<T> clazz) {
		MongoCollection<T> mongoCollection = getCollection(clazz);
		AggregateIterable<T> iterable = mongoCollection.aggregate(filters);
		if (iterable == null) {
			return null;
		}
		return iterable.first();
	}

	/**
	 * 查询多个对象
	 * 
	 * @param query
	 * @param clazz
	 * @return
	 */
	public <T> List<T> findMany(List<Bson> filters, Class<T> clazz) {
		MongoCollection<T> mongoCollection = getCollection(clazz);
		AggregateIterable<T> iterable = mongoCollection.aggregate(filters);
		if (iterable == null) {
			return null;
		}
		List<T> result = new ArrayList<T>();
		for (T t : iterable) {
			result.add(t);
		}
		return result;
	}

	/**
	 * 分页查询多个对象
	 * 
	 * @param query
	 * @param clazz
	 * @return
	 */
	public <T> PageResult<T> findManyByPage(List<Bson> filters, Class<T> clazz, PageResult<T> page) {
		MongoCollection<T> mongoCollection = getCollection(clazz);

		// 跳过记录数
		int skip = page.getPageNum() == 1 ? 0 : page.getPrevPage() * page.getPageSize();

		long total = mongoCollection.count(Projections.fields(filters));
		FindIterable<T> iterable = mongoCollection.find(Projections.fields(filters)).skip(skip)
				.limit(page.getPageSize());
		if (iterable == null) {
			return null;
		}
		List<T> result = new ArrayList<T>();
		for (T t : iterable) {
			result.add(t);
		}

		PageResult<T> pageResult = new PageResult<T>();
		pageResult.setList(result);
		pageResult.setTotal((int)total);
		return pageResult;
	}

	/**
	 * 获取数据库集合，collectionName不存在时会自动创建
	 * 
	 * @param collectionName
	 * @return
	 */
	private <T> MongoCollection<T> getCollection(String collectionName, Class<T> clazz) {
		return getDatabase().getCollection(collectionName, clazz);
	}
	
	/**
	 * 根据collection获取MongoCollection
	 * @param collectionName
	 * @return
	 */
	private MongoCollection<Document> getCollection(String collectionName) {
		return getDatabase().getCollection(collectionName);
	}
	
	/**
	 * 删除collection
	 * @param collectionName
	 */
	public void dropCollection(String collectionName) {
		getDatabase().getCollection(collectionName).drop();
	}

	/**
	 * 获取数据库集合，使用配置的集合名，collectionName不存在时会自动创建
	 * 
	 * @param collectionName
	 * @return
	 */
	private <T> MongoCollection<T> getCollection(Class<T> clazz) {
		return getDatabase().getCollection(mongoConfig.getCollectionName(), clazz);
	}
	
	public MongoClient getMongoClient() {
		return mongoClient;
	}

	public void setMongoClient(MongoClient mongoClient) {
		this.mongoClient = mongoClient;
	}

	public MongoDatabase getDatabase() {
		return database;
	}

	public void setDatabase(MongoDatabase database) {
		this.database = database;
	}

	public MongoConfig getMongoConfig() {
		return mongoConfig;
	}

	public void setMongoConfig(MongoConfig mongoConfig) {
		this.mongoConfig = mongoConfig;
	}

}
